/*
 *  (c) 2015 fullhan.com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

/*****************************************************************************
 *  Include Section
 *  add all #include here
 *****************************************************************************/
#include <common.h>
#include <clk.h>
#include <dm.h>
#include <malloc.h>
#include <sdhci.h>
#include <asm/arch/hardware.h>
#include <mmc.h> 
#include <fh_mshc_lite.h>
#include <fh_mshc_lite_dll.h>
/*****************************************************************************
 * Define section
 * add all #define here
 *****************************************************************************/

#define EMMC_PHY_VERSION_ID                 0x00
#define EMMC_PHY_INT_EN                     0x04
#define EMMC_PHY_INT_SRC                    0x08
#define EMMC_PHY_CTRL                       0x0C
#define EMMC_PHY_TEST_O                     0x10
#define EMMC_PHY_TEST_I                     0x14
#define EMMC_PHY_IE_CTRL                    0x18
#define EMMC_PHY_DL_CTRL                    0x1C
#define EMMC_PHY_CTRL2                      0x24


#define DLL_SCAN_TX_STEP	0x10
#define DLL_SCAN_HS400_TX_STEP	0x8

#define ACTIVE_SEED     0x55
#define INACTIVE_SEED   0xaa

#define FH_MSHC_LITE_ASSERT(expr) if (!(expr)) { \
        printf("Assertion failed! %s:line %d\n", \
        __func__, __LINE__); \
        while (1)   \
           ;       \
        }
#define MAX_AUTO_TEST_TIMES	10
#define AUTO_TUNING_PARA_ALREADY_GOT 0x1
#define AUTO_TUNING_PARA_NULL 0x0
/****************************************************************************
 * ADT section
 *  add definition of user defined Data Type that only be used in this file  here
 ***************************************************************************/
struct auto_dll_seed{
	u32 flag;
	u32 rx_cmd_seed;
	u32 tx_cmd_ret;
};

struct auto_dll_result{
	#define ACTIVE_RESULT           0x55
	#define INACTIVE_RESULT         0//memset to inactice.
	u32 flag;
	u32 tx_data;
	u32 rx_cmd;
	u32 rx_data;
};

/*****************************************************************************
 *  static fun;
 *****************************************************************************/
static void fh_mshc_set_ios_post(struct sdhci_host *host);
static void fh_mshc_set_clock(struct sdhci_host *host, u32 clock);
static void dump_result(void);
/*****************************************************************************
 *  extern fun;
 *****************************************************************************/
extern void sdhci_do_enable_es_mode(struct sdhci_host *host, u32 enable);
extern void sdhci_do_enable_v4_mode(struct sdhci_host *host);
extern void sdhci_reset(struct sdhci_host *host, u8 mask);
/*****************************************************************************
 * Global variables section - Local
 * define global variables(will be refered only in this file) here,
 * static keyword should be used to limit scope of local variable to this file
 * e.g.
 *  static uint8_t ufoo;
 *****************************************************************************/
struct fh_mshc_lite *g_fh_mshc_obj = 0;
static u8 g_mshc_buf[512] = {0};
u8 g_mshc_ext_data[512] = {0};
struct rec_dl_info dl_info[MAX_RX_DELAY_LINE_SIZE];
struct auto_dll_seed dll_seed_array[MAX_RX_DELAY_LINE_SIZE / DLL_SCAN_TX_STEP] = {0};
struct auto_dll_seed hs400_dll_seed_array[MAX_RX_DELAY_LINE_SIZE / DLL_SCAN_HS400_TX_STEP] = {0};
struct auto_dll_result auto_dll_result_array[MAX_AUTO_TEST_TIMES] = {0};
struct sdhci_ops dri_ops = {
	.set_clock	= fh_mshc_set_clock,
	.set_ios_post = fh_mshc_set_ios_post,
};

char *mmc_state_str[] = {
	"Idle",
	"Ready",
	"Ident",
	"Stby",
	"Tran",
	"Data",
	"Rcv",
	"Prg",
	"Dis",
	"Btst",
	"Slp",
	"reserved",
	"reserved",
	"reserved",
	"reserved",
	"reserved",
};

char *mmc_eol_str[] = {
	"not defined",
	"Normal",
	"Consumed 80% of reserved block",
	"Urgent",
};

char *mmc_life_str[] = {
	"not defined",
	"0% - 10% device life time used",
	"10% -20% device life time used",
	"20% -30% device life time used",
	"30% - 40% device life time used",
	"40% - 50% device life time used",
	"50% - 60% device life time used",
	"60% - 70% device life time used",
	"70% - 80% device life time used",
	"80% - 90% device life time used",
	"90% - 100% device life time used",
	"Exceeded its maximum estimated device life time",
	"Reserved",
};

char *mmc_device_type_str[] = {
	"High-Speed 26MHz - at rated device voltage(s)",
	"High-Speed 52MHz - at rated device voltage(s)",
	"High-Speed Dual Data 52MHz - 1.8V or 3V I/O",
	"High-Speed Dual Data 52MHz - 1.2V I/O",
	"HS200 Single Data 200 MHz - 1.8V I/O",
	"HS200 Single Data 200 MHz - 1.2V I/O",
    "HS400 Dual Data Rate 200MHz - 1.8V I/O",
    "HS400 Dual Data Rate 200MHz - 1.2V I/O",
};

char *mmc_version_str[] = {
	"Revision 1.0 (for MMC v4.0)",
	"Revision 1.1 (for MMC v4.1)",
	"Revision 1.2 (for MMC v4.2)",
	"Revision 1.3 (for MMC v4.3)",
	"Revision 1.4 (Obsolete)",
	"Revision 1.5 (for MMC v4.41)",
	"Revision 1.6 (for MMC v4.5, v4.51)",
	"Revision 1.7 (for MMC v5.0)",
	"Revision 1.8 (for MMC v5.1)",

};

char *mmc_timing_str[] = {
	"Selecting backwards compatibility interface timing",
	"High Speed",
	"HS200",
	"HS400",
};

char *mmc_width_str[] = {
	"1 bit data bus",
	"4 bit data bus",
	"8 bit data bus",
	"Reserved",
	"Reserved",
	"4 bit data bus (dual data rate)",
	"8 bit data bus (dual data rate)",	
};

char *mmc_partition_str[] = {
	"No access to boot partition (default)",
	"R/W boot partition 1",
	"R/W boot partition 2",
	"R/W Replay Protected Memory Block (RPMB)",
	"Access to General Purpose partition 1",
	"Access to General Purpose partition 2",
	"Access to General Purpose partition 3",
	"Access to General Purpose partition 4",	
};

char *mmc_boot_partition_str[] = {
	"Device not boot enabled (default)",
	"Boot partition 1 enabled for boot",
	"Boot partition 2 enabled for boot",
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",
	"User area enabled for boot",
};

char *mmc_boot_ack_str[] = {
	"No boot acknowledge sent (default)",
	"Boot acknowledge sent during boot operation Bit",
};

/*****************************************************************************
 * func below..
 *****************************************************************************/
void dump_est_csd_reg(u8 *p_u8){
	u32 total_cnt;
	u8 es_sup;
	int i;
	//life... (267 : 268 : 269)
	//boot size... (226)
	//total size... (212 : 215)
	//dev mode... (196)
	//version... (192)
	//timng... (185)
	//width... (183)
	//boot area...(179)
	printf("EOL INFO :: %s\n",mmc_eol_str[p_u8[267] & 0x03]);
	printf("TYPE_B life :: %s\n",mmc_life_str[p_u8[269]]);
	printf("TYPE_A life :: %s\n",mmc_life_str[p_u8[268]]);
	printf("Boot size :: %dKB\n",p_u8[226] * 128);
	total_cnt = p_u8[215] << 24 | p_u8[214] << 16 | p_u8[213] << 8 | p_u8[212];
	//totalsize = total_cnt * 512B
	//change to Mb = total_cnt / 2 / 1024
	printf("total size :: %dMB\n",total_cnt / 2048);
	printf("device support type\n");
	printf("{{{\n");
	for(i = 0; i <= 7; i++){
		if(p_u8[196] & (1 << i)){
			printf("%s\n",mmc_device_type_str[i]);
		}
	}
	printf("}}}\n");
	printf("device version :: %s\n",mmc_version_str[p_u8[192]]);
	printf("device timing :: %s\n",mmc_timing_str[p_u8[185] & 0xf]);
	//data width W/E_P...read pass
	//printf("device width :: %s\n",mmc_width_str[p_u8[183] & 0x7f]);

	es_sup = p_u8[EXT_CSD_STROBE_SUPPORT];
	printf("support %s hs400 mode\n", 
	es_sup == 1 ? "es" : "classics");

	printf("device part access :: %s\n",mmc_partition_str[p_u8[179] & 0x7]);
	printf("device boot part :: %s\n",mmc_boot_partition_str[(p_u8[179] >> 3) & 0x7]);
	printf("device boot ack :: %s\n",mmc_boot_ack_str[(p_u8[179] >> 6) & 0x1]);
}

void phy_tx_dri_clk_sel(struct fh_mshc_lite *p_mshc, u32 sel){
	if(sel == PHY_TX_DRI_SEL_CLK_EMMC){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 0, 1);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1 << 1, 3 << 1);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1, 1);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1 << 2, 3 << 1);
	}
}

void phy_tx_dri_delay(struct fh_mshc_lite *p_mshc, u32 delay){

	if(delay == PHY_BY_PASS_DELAY_FUNC){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 15, 1 << 15);
	}else{
		
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_DL_CTRL, delay, 0xff);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0 << 15, 1 << 15);
	}
}

void phy_tx_dri_inv_set(struct fh_mshc_lite *p_mshc, u32 enable){
	if(enable == PHY_TX_DRI_INV_ENABLE){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 9, 1 << 9);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 9);
	}
}

void phy_tx_for_card_clk_settting(struct fh_mshc_lite *p_mshc, u32 enable){
	if(enable == 1)
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 10, 1 << 10);
	else
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 10);
}

void phy_tx_for_drive_clk_gate(struct fh_mshc_lite *p_mshc){
	SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 0, 3 << 1);
}


void phy_rx_data_sample_clk_sel(struct fh_mshc_lite *p_mshc, u32 sel){
	if(sel == PHY_RX_CMD_SEL_CLK_EMMC){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 11);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 11, 1 << 11);
	}
}

void phy_rx_data_sample_delay(struct fh_mshc_lite *p_mshc, u32 delay){
	if(delay == PHY_BY_PASS_DELAY_FUNC){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 16, 1 << 16);
	}else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_DL_CTRL, delay << 8, 0xff << 8);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0 << 16, 1 << 16);
	}
}

void phy_rx_data_sample_inv_set(struct fh_mshc_lite *p_mshc, u32 enable){
	if(enable == PHY_RX_DATA_SAMPLE_INV_ENABLE){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 13, 1 << 13);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 13);
	}
}

void phy_rx_cmd_sample_clk_sel(struct fh_mshc_lite *p_mshc, u32 sel){
	if(sel == PHY_RX_CMD_SEL_CLK_EMMC){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 12);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 12, 1 << 12);
	}
}

void phy_rx_cmd_sample_delay(struct fh_mshc_lite *p_mshc, u32 delay){
	if(delay == PHY_BY_PASS_DELAY_FUNC){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 17, 1 << 17);
	}else{
		
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_DL_CTRL, delay << 16, 0xff << 16);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0 << 17, 1 << 17);
	}
}

void phy_rx_cmd_sample_inv_set(struct fh_mshc_lite *p_mshc, u32 enable){
	if(enable == PHY_RX_CMD_SAMPLE_INV_ENABLE){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 14, 1 << 14);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 14);
	}
}

void phy_bit_mode_sel(struct fh_mshc_lite *p_mshc, u32 sel){
	SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, sel << 29, 3 << 29);
}

void phy_intr_en_set(struct fh_mshc_lite *p_mshc, u32 val){
	SET_REG_M(p_mshc->phy_regs + EMMC_PHY_INT_EN, val, 0x0f);
}

void phy_intr_src_set(struct fh_mshc_lite *p_mshc, u32 val){
	SET_REG_M(p_mshc->phy_regs + EMMC_PHY_INT_SRC, val, 0x0f);
}

void phy_clk_stop_en_set(struct fh_mshc_lite *p_mshc, u32 enable){
	if(enable == 1){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1 << 3, 1 << 3);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 0, 1 << 3);
	}
}

void phy_tx_clk_src_sel(struct fh_mshc_lite *p_mshc, u32 sel){

	if(sel == PHY_TX_DRI_SEL_CLK_EMMC){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 0, 1);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1 << 1, 3 << 1);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1, 1);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1 << 2, 3 << 1);
	}
}


void phy_rx_sample_enable_set(struct fh_mshc_lite *p_mshc, u32 enable){
	if(enable == 1){
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 31, 1 << 31);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 28, 1 << 28);
	}
	else{
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 28);
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1 << 31);
	}
}

void phy_clear_rxfifo(struct fh_mshc_lite *p_mshc){
	SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 1 << 27, 1 << 27);
	SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0 << 27, 1 << 27);
}

void phy_clkstop_func_enable(struct fh_mshc_lite *p_mshc, u32 enable){
	if(enable == 1)
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 1 << 3, 1 << 3);
	else
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL2, 0, 1 << 3);
}


void phy_func_mode_set(struct fh_mshc_lite *p_mshc){

	SET_REG_M(p_mshc->phy_regs + EMMC_PHY_CTRL, 0, 1);
}


void reset_card_setting(struct fh_mshc_lite *p_mshc, u32 enable){

	if(enable  == 1)
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_IE_CTRL, 0, 1 << 9);
	else
		SET_REG_M(p_mshc->phy_regs + EMMC_PHY_IE_CTRL, 1 << 9, 1 << 9);
}

void phy_dly_setting(struct fh_mshc_lite *p_mshc, u32 tx_dly, u32 rxdata_dly, u32 rxcmd_dly){
	// phy_tx_for_card_clk_settting(p_mshc, 0);
	// mdelay(1);
	//do not close rx sample en...
	//phy_rx_sample_enable_set(p_mshc, 0);
	phy_tx_dri_delay(p_mshc, tx_dly);
	phy_rx_data_sample_delay(p_mshc, rxdata_dly);
	phy_rx_cmd_sample_delay(p_mshc, rxcmd_dly);
	//phy_clear_rxfifo(p_mshc);
	phy_rx_sample_enable_set(p_mshc, 1);
	phy_tx_for_card_clk_settting(p_mshc, 1);
	//mdelay(1);
}

static void fh_mshc_set_clock(struct sdhci_host *host, u32 clock)
{

}

void fh_mshc_tuning_ctrl_init(struct sdhci_host *host)
{
	u32 isr_set = SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK;
	sdhci_reset(host, SDHCI_RESET_ALL);

	if(host->flags & SDHCI_AUTO_CMD12)
		isr_set |= SDHCI_INT_ACMD12ERR;

	/* Enable only interrupts served by the SD controller */
	sdhci_writel(host, isr_set, SDHCI_INT_ENABLE);
	/* Mask all sdhci interrupt sources */
	sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE);
	sdhci_writew(host, SDHCI_CARD_IS_EMMC, SDHCI_EMMC_CTRL);

	sdhci_do_enable_v4_mode(host);
}


void fh_mshc_tuning_ctrl_set_width_and_clkmode(struct sdhci_host *host,  u32 clk, u32 timing, u32 width)
{
	u32 ctrl;
	u16 ctrl_2;
	/* Set bus width */
	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
	if (width == 8) {
		ctrl &= ~SDHCI_CTRL_4BITBUS;
		ctrl |= SDHCI_CTRL_8BITBUS;
	} else {
		ctrl &= ~SDHCI_CTRL_8BITBUS;
		if (width == 4)
			ctrl |= SDHCI_CTRL_4BITBUS;
		else
			ctrl &= ~SDHCI_CTRL_4BITBUS;
	}

	if (clk > 26000000)
		ctrl |= SDHCI_CTRL_HISPD;
	else
		ctrl &= ~SDHCI_CTRL_HISPD;


	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);

	/*set timing*/
	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
	/* Select Bus Speed Mode for host */
	ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;

	if(timing == MMC_TIMING_MMC_HS200)
		ctrl_2 |= 3;
	else if(timing == MMC_TIMING_MMC_HS400)
		ctrl_2 |= 7;
	else
		ctrl_2 |= 1;

	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);

}

void fh_mshc_tuning_set_clock(struct sdhci_host *host, u32 clock, u32 timing, u32 width, u32 es_mode)
{
	struct fh_mshc_lite *p_mshc;
	p_mshc = host->dri_pri;

	if(p_mshc->plat->clk_set)
		p_mshc->plat->clk_set(host, clock);
	phy_clear_rxfifo(p_mshc);
	fh_mshc_tuning_ctrl_set_width_and_clkmode(host,  clock, timing, width);

	if(timing == MMC_TIMING_MMC_HS400 && es_mode == EMMC_ES_MODE_ENABLE){
		//printf("es mode enable...\n");
		sdhci_do_enable_es_mode(host, 1);
	}
	else{
		sdhci_do_enable_es_mode(host, 0);
	}

	if(timing == MMC_TIMING_MMC_HS400){
		phy_tx_dri_clk_sel(p_mshc, PHY_TX_DRI_SEL_CLK_EMMC_2X);
		if(es_mode == EMMC_ES_MODE_ENABLE)
			phy_rx_cmd_sample_clk_sel(p_mshc, PHY_RX_CMD_SEL_CLK_DS);
		else
			phy_rx_cmd_sample_clk_sel(p_mshc, PHY_RX_CMD_SEL_CLK_EMMC);
		phy_rx_data_sample_clk_sel(p_mshc, PHY_RX_DATA_SEL_CLK_DS);
	}
	else{
		phy_tx_dri_clk_sel(p_mshc, PHY_TX_DRI_SEL_CLK_EMMC);
		phy_rx_data_sample_clk_sel(p_mshc, PHY_RX_DATA_SEL_CLK_EMMC);
		phy_rx_cmd_sample_clk_sel(p_mshc, PHY_RX_CMD_SEL_CLK_EMMC);
	}
}


void fh_mshc_hs_setting(struct sdhci_host *host, u32 clock, u32 width, u32 es_mode, u32 timing, struct fh_mshc_phy_dll *p_dll){
	struct fh_mshc_lite *p_mshc;
	p_mshc = host->dri_pri;

	phy_dly_setting(p_mshc, p_dll->tx_dly, p_dll->rxdata_dly,p_dll->rxcmd_dly);
	fh_mshc_tuning_set_clock(host, clock, timing, width, es_mode);
}

void fh_mshc_hs200_setting(struct sdhci_host *host, u32 clock, u32 width, u32 es_mode, u32 timing, struct fh_mshc_phy_dll *p_dll){
	struct fh_mshc_lite *p_mshc;
	p_mshc = host->dri_pri;	

	//first set clk
	fh_mshc_tuning_set_clock(host, clock, timing, width, es_mode);
	//then set dll
	phy_dly_setting(p_mshc, p_dll->tx_dly, p_dll->rxdata_dly,p_dll->rxcmd_dly);
	phy_clear_rxfifo(p_mshc);
}

void fh_mshc_hs400_setting(struct sdhci_host *host, u32 clock, u32 width, u32 es_mode, u32 timing, struct fh_mshc_phy_dll *p_dll){
	struct fh_mshc_lite *p_mshc;
	p_mshc = host->dri_pri;	
	//first set dll
	phy_dly_setting(p_mshc, p_dll->tx_dly, p_dll->rxdata_dly,p_dll->rxcmd_dly);
	//then set clk..
	fh_mshc_tuning_set_clock(host, clock, timing, width, es_mode);
}


static void fh_mshc_set_ios_post(struct sdhci_host *host)
{
	struct mmc *mmc = host->mmc;
	struct fh_mshc_lite *p_mshc;
	struct mshc_lite_emmc_card_info *p_info = 0;
	struct mshc_lite_plat_info *plat_info;
	u32 clock;
	struct fh_mshc_phy_dll dll = {0};

	p_mshc = host->dri_pri;
	clock = mmc->clock;
	plat_info = p_mshc->plat;
	p_info = p_mshc->bind_active_tuning_emmc;
	if(!p_info){
		printf("DO NOT BYPASS auto tuning process....\n");
		printf("emmc no tuning ....clk [%d]may cause err..\n",clock);
		FH_MSHC_LITE_ASSERT(0);
	}
	//printf("%s :: clk : timing : bus_width : strobe_enhanced = %d : %x : %d : %d\n",__func__,clock, mmc->timing, mmc->bus_width,mmc->strobe_enhanced);
	if(mmc->timing == MMC_TIMING_MMC_HS400 && clock != 200000000)
		return;
	if (clock == 0)
		return;

	if(plat_info->soc_ctrl_reset)
		plat_info->soc_ctrl_reset(host);

	mshc_phy_init(p_mshc, INIT_INV_DISABLE);
#if(0)
	if(clock > 52000000)
		mshc_phy_init(p_mshc, INIT_INV_DISABLE);
	else
		mshc_phy_init(p_mshc, INIT_INV_ENABLE);
#endif
	fh_mshc_tuning_ctrl_init(host);

	if(mmc->timing == MMC_TIMING_MMC_HS400){
		dll.tx_dly = p_info->hs400_tx_data;
		dll.rxdata_dly = p_info->hs400_rx_data;
		dll.rxcmd_dly = p_info->hs400_rx_cmd;
		fh_mshc_hs400_setting(host, clock, mmc->bus_width, mmc->strobe_enhanced, mmc->timing, &dll);
	}
	else{
		if(clock > 52000000){
			dll.tx_dly = p_info->hs200_tx_data;
			dll.rxdata_dly = p_info->hs200_rx_data;
			dll.rxcmd_dly = p_info->hs200_rx_cmd;
			fh_mshc_hs200_setting(host, clock, mmc->bus_width, mmc->strobe_enhanced, mmc->timing, &dll);
		}
		else{
			dll.tx_dly = p_info->hs_tx_data;
			dll.rxdata_dly = p_info->hs_rx_data;
			dll.rxcmd_dly = p_info->hs_rx_cmd;
			fh_mshc_hs_setting(host, clock, mmc->bus_width, mmc->strobe_enhanced, mmc->timing, &dll);
		}
	}

}


struct mshc_lite_emmc_card_info * emmc_name_to_plat_info(struct mshc_lite_emmc_card_info *info, char *emmc_name){

	struct mshc_lite_emmc_card_info *p_info = info;
	for(; p_info->mmc_name != 0; p_info++){
		//printf("process p_info name is %s\n",p_info->mmc_name);
		if(strcmp(emmc_name, p_info->mmc_name) == 0){
			//printf("got board tuning [%s]\n", p_info->mmc_name);
			break;
		}
	}
	if(!p_info->mmc_name){
		printf("warning :: %s may not support....\n",emmc_name);
		p_info = 0;
	}
	return p_info;
}


void mshc_phy_init(struct fh_mshc_lite *p_mshc, int inv_en){

	phy_rx_sample_enable_set(p_mshc, 0);
	phy_intr_src_set(p_mshc, 0);
	phy_tx_for_drive_clk_gate(p_mshc);
	phy_func_mode_set(p_mshc);

	if(inv_en == INIT_INV_ENABLE){
		phy_tx_dri_inv_set(p_mshc, PHY_TX_DRI_INV_ENABLE);
		phy_rx_data_sample_inv_set(p_mshc, PHY_RX_DATA_SAMPLE_INV_ENABLE);
		phy_rx_cmd_sample_inv_set(p_mshc, PHY_RX_CMD_SAMPLE_INV_ENABLE);
	}
	else{
		phy_tx_dri_inv_set(p_mshc, PHY_TX_DRI_INV_DISABLE);
		phy_rx_data_sample_inv_set(p_mshc, PHY_RX_DATA_SAMPLE_INV_DISABLE);
		phy_rx_cmd_sample_inv_set(p_mshc, PHY_RX_CMD_SAMPLE_INV_DISABLE);
	}

	phy_tx_dri_clk_sel(p_mshc, PHY_TX_DRI_SEL_CLK_EMMC);
	phy_rx_data_sample_clk_sel(p_mshc, PHY_RX_DATA_SEL_CLK_EMMC);
	phy_rx_cmd_sample_clk_sel(p_mshc, PHY_RX_CMD_SEL_CLK_EMMC);
	phy_clk_stop_en_set(p_mshc, 1);
	phy_bit_mode_sel(p_mshc, PHY_MAX_BIT_WIDTH_8);
	phy_dly_setting(p_mshc, PHY_BY_PASS_DELAY_FUNC, PHY_BY_PASS_DELAY_FUNC, PHY_BY_PASS_DELAY_FUNC);

}

static void print_mmcinfo(struct mmc *mmc)
{
	printf("MMC/SD Card:\n");
	printf("    MID:         0x%x\n", mmc->cid[0] >> 24);
	printf("    Read Block:  %d Bytes\n", mmc->read_bl_len);
	printf("    Write Block: %d Bytes\n", mmc->write_bl_len);
	printf("    Chip Size:   %s Bytes (%s)\n",
			ultohstr(mmc->capacity),
			mmc->high_capacity ? "High Capacity" : "Low Capacity");
	printf("    Name:        \"%c%c%c%c%c%c\"\n",
			mmc->cid[0] & 0xff,
			(mmc->cid[1] >> 24),
			(mmc->cid[1] >> 16) & 0xff,
			(mmc->cid[1] >> 8) & 0xff,
			mmc->cid[1] & 0xff, (mmc->cid[2] >> 24) & 0xff);

	printf("    Chip Type:   %s\n"
			"    Version:     %d.%d\n",
			IS_SD(mmc) ? "SD" : "MMC",
			EXTRACT_SDMMC_MAJOR_VERSION(mmc->version),
			EXTRACT_SDMMC_MINOR_VERSION(mmc->version));

	printf("    Speed:       %sHz\n", ultohstr(mmc->clock));
	printf("    Bus Width:   %dbit\n", mmc->bus_width);
	printf("    Mode:        %s\n", mmc->strobe_enhanced ? "HS400ES" :
			mmc->timing == MMC_TIMING_MMC_HS400 ? "HS400" :
			mmc->timing == MMC_TIMING_MMC_HS200 ? "HS200" : "HS");
}


int __fh_mshc_phy_io_dll_auto_scan_hs_with_clk(struct sdhci_host *host, struct mshc_lite_plat_info *plat_info, u32 tuning_mode,
struct auto_dll_seed *p_seed, u32 seed_array_size){
#if(1)
	int ret;
	u32 temp_dl;
	u32 old_best_winlen;
	u32 new_best_winlen;
	u32 temp_best_dl;
	u32 best_tx_cmd_dl;
	u32 best_rx_data_dl;
	u32 best_tx_data_dl;
	u32 best_rx_cmd_dl;
	u32 step_size;
	u32 emmc_do_init;
	u32 i = 0;
	u32 active_seed_index = 0;

	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;
	struct fh_mshc_lite *p_mshc;

	step_size = 4;
	old_best_winlen = 0;
	new_best_winlen = 0;
	temp_best_dl = 0;
	p_mshc = host->dri_pri;
	emmc_pinfo = p_mshc->bind_active_tuning_emmc;

	emmc_do_init = 1;
	for(i = 0; i < seed_array_size; i++){
		if(p_seed[i].flag == INACTIVE_SEED){
			printf("pass inactive seed [%d] : %x\n", i ,p_seed[i].rx_cmd_seed);
			continue;
		}
		temp_dl = p_seed[i].rx_cmd_seed;
		mshc_auto_hs_txcmd_dl_rec(host, dl_info, MAX_TX_DELAY_LINE_SIZE, temp_dl, step_size, emmc_do_init, 0, tuning_mode);
		//int emmc_parse_dl_rec(struct rec_dl_info *p_info, int rec_dl_size, int *p_ret_dl, int tx_or_rx, int *p_ret_win_len);
		ret = emmc_parse_dl_rec(dl_info, MAX_TX_DELAY_LINE_SIZE, &temp_best_dl, PARSE_TX_DL_REC, &new_best_winlen);
		if(ret == 0){
			//printf("rx cmd [0x%02x] :: got best tx dll is [0x%x] : winlen [%d]\n",temp_dl, temp_best_dl, new_best_winlen);
			if(old_best_winlen < new_best_winlen){
				//update
				old_best_winlen = new_best_winlen;
				best_tx_cmd_dl = temp_best_dl;
				//test below use.....
				active_seed_index = i;
			}
		}
		else{
			//signed this seed inactive....
			printf("[%d] :: rx seed [%x] err..\n",i, p_seed[i].rx_cmd_seed);
			p_seed[i].flag = INACTIVE_SEED;
		}
	}


	if(old_best_winlen == 0){
		printf("got no best tx cmd......all rx seed failed...maybe next time will got..\n");
		goto AUTO_SCAN_ERR_PROCESS;
	}

	//best_tx_cmd_dl += TX_DATA_OFFSET_WITH_CMD;
	emmc_do_init = 1;
	mshc_auto_hs_rxcmd_dl_rec(host, dl_info, MAX_RX_DELAY_LINE_SIZE,
	best_tx_cmd_dl, step_size, emmc_do_init, 0, tuning_mode);
	ret = emmc_parse_dl_rec(dl_info, MAX_RX_DELAY_LINE_SIZE, &best_rx_cmd_dl, PARSE_RX_DL_REC, &new_best_winlen);
	if(ret == 0){
		//printf("got best rx cmd dll is [0x%x]\n", best_rx_cmd_dl);
	}
	else{
		printf("failed got rx cmd dll...\n");
		p_seed[active_seed_index].flag = INACTIVE_SEED;
		goto AUTO_SCAN_ERR_PROCESS;
	}

	mshc_auto_hs_rxdata_dl_rec(host, dl_info, MAX_RX_DELAY_LINE_SIZE,
	best_rx_cmd_dl, best_tx_cmd_dl, step_size, tuning_mode);
	ret = emmc_parse_dl_rec(dl_info, MAX_RX_DELAY_LINE_SIZE, &best_rx_data_dl, PARSE_RX_DL_REC, &new_best_winlen);
	if(ret == 0){
		//printf("got best hs200 rx dll is [0x%x]\n", best_rx_data_dl);
	}
	else{
		printf("failed got rx data dll...\n");
		p_seed[active_seed_index].flag = INACTIVE_SEED;
		goto AUTO_SCAN_ERR_PROCESS;
	}

	best_tx_data_dl = best_tx_cmd_dl;
	ret = mshc_hs_auto_fix_test(host, best_tx_data_dl, best_rx_cmd_dl,
	best_rx_data_dl, AUTO_FIX_TEST_PASS_TIME, tuning_mode);
	if(ret != 0){
		printf("failed fix test...\n");
		p_seed[active_seed_index].flag = INACTIVE_SEED;
		goto AUTO_SCAN_ERR_PROCESS;
	}

	emmc_pinfo->hs_tx_data = best_tx_data_dl;
	emmc_pinfo->hs_rx_cmd = best_rx_cmd_dl;
	emmc_pinfo->hs_rx_data = best_rx_data_dl;


	return 0;

AUTO_SCAN_ERR_PROCESS:
	return -1;
#endif
}




int __fh_mshc_phy_io_dll_auto_scan_hs200_with_clk(struct sdhci_host *host, struct mshc_lite_plat_info *plat_info, u32 tuning_mode,
struct auto_dll_seed *p_seed, u32 seed_array_size){
	int ret;
	u32 temp_dl;
	u32 old_best_winlen;
	u32 new_best_winlen;
	u32 temp_best_dl;
	u32 best_tx_cmd_dl;
	u32 best_rx_data_dl;
	u32 best_tx_data_dl;
	u32 best_rx_cmd_dl;
	u32 step_size;
	u32 emmc_do_init;
	u32 i = 0;
	u32 active_seed_index = 0;

	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;
	struct fh_mshc_lite *p_mshc;

	step_size = 4;
	old_best_winlen = 0;
	new_best_winlen = 0;
	temp_best_dl = 0;
	p_mshc = host->dri_pri;
	emmc_pinfo = p_mshc->bind_active_tuning_emmc;

	emmc_do_init = 1;
	for(i = 0; i < seed_array_size; i++){
		if(p_seed[i].flag == INACTIVE_SEED){
			printf("pass inactive seed [%d] : %x\n", i ,p_seed[i].rx_cmd_seed);
			continue;
		}
		temp_dl = p_seed[i].rx_cmd_seed;
		mshc_auto_hs200_txcmd_dl_rec(host, dl_info, MAX_TX_DELAY_LINE_SIZE, temp_dl, step_size, emmc_do_init, 0, tuning_mode);
		//int emmc_parse_dl_rec(struct rec_dl_info *p_info, int rec_dl_size, int *p_ret_dl, int tx_or_rx, int *p_ret_win_len);
		ret = emmc_parse_dl_rec(dl_info, MAX_TX_DELAY_LINE_SIZE, &temp_best_dl, PARSE_TX_DL_REC, &new_best_winlen);
		if(ret == 0){
			//printf("rx cmd [0x%02x] :: got best tx dll is [0x%x] : winlen [%d]\n",temp_dl, temp_best_dl, new_best_winlen);
			if(old_best_winlen < new_best_winlen){
				//update
				old_best_winlen = new_best_winlen;
				best_tx_cmd_dl = temp_best_dl;
				//test below use.....
				active_seed_index = i;
			}
		}
		else{
			//signed this seed inactive....
			printf("[%d] :: rx seed [%x] err..\n",i, p_seed[i].rx_cmd_seed);
			p_seed[i].flag = INACTIVE_SEED;
		}
	}


	if(old_best_winlen == 0){
		printf("got no best tx cmd......all rx seed failed...maybe next time will got..\n");
		goto AUTO_SCAN_ERR_PROCESS;
	}

	best_tx_cmd_dl += TX_DATA_OFFSET_WITH_CMD;
	emmc_do_init = 1;
	mshc_auto_hs200_rxcmd_dl_rec(host, dl_info, MAX_RX_DELAY_LINE_SIZE,
	best_tx_cmd_dl, step_size, emmc_do_init, 0, tuning_mode);
	ret = emmc_parse_dl_rec(dl_info, MAX_RX_DELAY_LINE_SIZE, &best_rx_cmd_dl, PARSE_RX_DL_REC, &new_best_winlen);
	if(ret == 0){
		//printf("got best rx cmd dll is [0x%x]\n", best_rx_cmd_dl);
	}
	else{
		printf("failed got rx cmd dll...\n");
		p_seed[active_seed_index].flag = INACTIVE_SEED;
		goto AUTO_SCAN_ERR_PROCESS;
	}

	mshc_auto_hs200_rxdata_dl_rec(host, dl_info, MAX_RX_DELAY_LINE_SIZE,
	best_rx_cmd_dl, best_tx_cmd_dl, step_size, tuning_mode);
	ret = emmc_parse_dl_rec(dl_info, MAX_RX_DELAY_LINE_SIZE, &best_rx_data_dl, PARSE_RX_DL_REC, &new_best_winlen);
	if(ret == 0){
		//printf("got best hs200 rx dll is [0x%x]\n", best_rx_data_dl);
	}
	else{
		printf("failed got rx data dll...\n");
		p_seed[active_seed_index].flag = INACTIVE_SEED;
		goto AUTO_SCAN_ERR_PROCESS;
	}

	best_tx_data_dl = best_tx_cmd_dl;
	printf("hs200 tx : rx_cmd : rx_data = %x : %x : %x\n",best_tx_data_dl, best_rx_cmd_dl, best_rx_data_dl);
	ret = mshc_hs200_auto_fix_test(host, best_tx_data_dl, best_rx_cmd_dl,
	best_rx_data_dl, AUTO_FIX_TEST_PASS_TIME, tuning_mode);
	if(ret != 0){
		printf("failed fix test...\n");
		p_seed[active_seed_index].flag = INACTIVE_SEED;
		goto AUTO_SCAN_ERR_PROCESS;
	}

	emmc_pinfo->hs200_tx_data = best_tx_data_dl;
	emmc_pinfo->hs200_rx_cmd = best_rx_cmd_dl;
	emmc_pinfo->hs200_rx_data = best_rx_data_dl;


	return 0;

AUTO_SCAN_ERR_PROCESS:
	return -1;
}


int __fh_mshc_phy_io_dll_auto_scan_hs200_classic(struct sdhci_host *host, struct mshc_lite_plat_info *plat_info, u32 tuning_mode,
struct auto_dll_seed *p_seed, u32 seed_array_size){

	int ret;
	u32 temp_dl;
	u32 old_best_winlen;
	u32 new_best_winlen;
	u32 temp_best_dl;
	u32 best_tx_cmd_dl;
	u32 best_rx_data_dl;
	u32 best_tx_data_dl;
	u32 best_rx_cmd_dl;
	u32 step_size;
	u32 emmc_do_init;
	u32 i = 0;
	u32 active_seed_index = 0;

	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;
	struct fh_mshc_lite *p_mshc;

	step_size = 4;
	old_best_winlen = 0;
	new_best_winlen = 0;
	temp_best_dl = 0;
	p_mshc = host->dri_pri;
	emmc_pinfo = p_mshc->bind_active_tuning_emmc;
	emmc_do_init = 1;
	for(i = 0; i < seed_array_size; i++){
		if(p_seed[i].flag == INACTIVE_SEED){
			printf("pass inactive seed [%d] : %x\n", i ,p_seed[i].rx_cmd_seed);
			continue;
		}
		temp_dl = p_seed[i].rx_cmd_seed;
		mshc_hs200_auto_classic_tuning(host, dl_info, MAX_TX_DELAY_LINE_SIZE, temp_dl, step_size, tuning_mode);
		ret = emmc_parse_dl_rec(dl_info, MAX_TX_DELAY_LINE_SIZE, &temp_best_dl, PARSE_TX_DL_REC, &new_best_winlen);
		if(ret == 0){
			//printf("rx cmd [0x%02x] :: got best tx dll is [0x%x] : winlen [%d]\n",temp_dl, temp_best_dl, new_best_winlen);
			if(old_best_winlen < new_best_winlen){
				//update
				old_best_winlen = new_best_winlen;
				best_tx_cmd_dl = temp_best_dl;
				best_rx_cmd_dl = temp_dl;
				//test below use.....
				active_seed_index = i;
			}
		}
		else{
			//signed this seed inactive....
			printf("[%d] :: rx seed [%x] err..\n",i, p_seed[i].rx_cmd_seed);
			p_seed[i].flag = INACTIVE_SEED;
		}
	}


	if(old_best_winlen == 0){
		printf("got no best hs200 classic tx data and rx cmd......all rx seed failed...maybe next time will got..\n");
		goto AUTO_SCAN_ERR_PROCESS;
	}

	emmc_pinfo->hs200_tx_data = best_tx_cmd_dl;
	emmc_pinfo->hs200_rx_cmd = best_rx_cmd_dl;
	emmc_pinfo->hs200_rx_data = best_rx_cmd_dl;

	return 0;

AUTO_SCAN_ERR_PROCESS:
	return -1;
}


int __fh_mshc_auto_get_hs400_classic_tx_rxcmd(struct sdhci_host *host, struct mshc_lite_plat_info *plat_info,
struct auto_dll_seed *p_seed, u32 seed_array_size){
	int ret;
	u32 temp_dl;
	u32 old_best_winlen;
	u32 new_best_winlen;
	u32 temp_best_dl;
	u32 best_tx_data_dl;
	u32 best_rx_cmd_dl;
	u32 step_size;
	u32 i = 0;
	u32 active_seed_index = 0;

	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;
	struct fh_mshc_lite *p_mshc;


	step_size = 2;
	old_best_winlen = 0;
	new_best_winlen = 0;
	temp_best_dl = 0;
	p_mshc = host->dri_pri;
	emmc_pinfo = p_mshc->bind_active_tuning_emmc;
	for(i = 0; i < seed_array_size; i++){
		if(p_seed[i].flag == INACTIVE_SEED){
			continue;
		}
		temp_dl = p_seed[i].rx_cmd_seed;
		mshc_auto_hs400_classic_txdata_dl_rec(host, dl_info, MAX_TX_DELAY_LINE_SIZE,
		temp_dl, emmc_pinfo->hs400_rx_data, step_size);

		ret = emmc_parse_dl_rec(dl_info, MAX_TX_DELAY_LINE_SIZE, &temp_best_dl, PARSE_TX_DL_REC, &new_best_winlen);
		if(ret == 0){
			//printf("rx cmd [0x%02x] :: got best rxdata dll is [0x%x] : winlen [%d]\n",temp_dl, temp_best_dl, new_best_winlen);
			if(old_best_winlen < new_best_winlen){
				//update
				old_best_winlen = new_best_winlen;
				best_tx_data_dl = temp_best_dl;
				best_rx_cmd_dl = temp_dl;
			}
		}
		else{
			//signed this seed inactive....
			//printf("[%d] :: rx seed err %x\n",i, p_seed[i].rx_cmd_seed);
			p_seed[i].flag = INACTIVE_SEED;
		}
	}
	if(old_best_winlen == 0){
		printf("got no best hs400 tx data and rx cmd......all rx seed failed...maybe next time will got..\n");
		goto AUTO_SCAN_ERR_PROCESS;
	}

	mshc_auto_hs400_classic_rxcmd_dl_rec(host, dl_info, MAX_RX_DELAY_LINE_SIZE, best_tx_data_dl, step_size, 0);
	ret = emmc_parse_dl_rec(dl_info, MAX_TX_DELAY_LINE_SIZE, &temp_best_dl, PARSE_RX_DL_REC, &new_best_winlen);
	if(ret == 0){
		best_rx_cmd_dl = temp_best_dl;
	}
	else{
		//signed this seed inactive....
		//printf("[%d] :: rx seed err %x\n",i, p_seed[i].rx_cmd_seed);
		p_seed[i].flag = INACTIVE_SEED;
	}
	//printf("best rx cmd dl is %x\n",best_rx_cmd_dl);
	ret = mshc_auto_hs400_classic_fix_test(host, best_tx_data_dl, best_rx_cmd_dl,
	emmc_pinfo->hs400_rx_data, AUTO_FIX_TEST_PASS_TIME);
	if(ret != 0){
		printf("failed hs400 classic fix test...\n");
		p_seed[active_seed_index].flag = INACTIVE_SEED;
		goto AUTO_SCAN_ERR_PROCESS;
	}

	emmc_pinfo->hs400_tx_data = best_tx_data_dl;
	emmc_pinfo->hs400_rx_cmd = best_rx_cmd_dl;

	return 0;

AUTO_SCAN_ERR_PROCESS:
	return -1;
}


int __fh_mshc_auto_get_hs400_classic_rx_data(struct sdhci_host *host, struct mshc_lite_plat_info *plat_info,
struct auto_dll_seed *p_seed, u32 seed_array_size){
	int ret;
	u32 temp_dl;
	u32 old_best_winlen;
	u32 new_best_winlen;
	u32 temp_best_dl;
	u32 best_rx_data_dl;
	u32 step_size;
	u32 i = 0;


	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;
	struct fh_mshc_lite *p_mshc;

	step_size = 2;
	old_best_winlen = 0;
	new_best_winlen = 0;
	temp_best_dl = 0;
	
	p_mshc = host->dri_pri;
	emmc_pinfo = p_mshc->bind_active_tuning_emmc;
	for(i = 0; i < seed_array_size; i++){
		if(p_seed[i].flag == INACTIVE_SEED){
			printf("pass inactive seed [%d] : %x\n", i ,p_seed[i].rx_cmd_seed);
			continue;
		}
		temp_dl = p_seed[i].rx_cmd_seed;
		//get rx data......
		mshc_auto_hs400_classic_rxdata_dl_rec(host, dl_info, MAX_RX_DELAY_LINE_SIZE,
		temp_dl, emmc_pinfo->hs200_tx_data, step_size);
		ret = emmc_parse_dl_rec(dl_info, MAX_RX_DELAY_LINE_SIZE, &temp_best_dl, PARSE_RX_DL_REC, &new_best_winlen);
		if(ret == 0){
			//printf("rx cmd [0x%02x] :: got best rxdata dll is [0x%x] : winlen [%d]\n",temp_dl, temp_best_dl, new_best_winlen);
			if(old_best_winlen < new_best_winlen){
				//update
				old_best_winlen = new_best_winlen;
				best_rx_data_dl = temp_best_dl;
			}
		}
		else{
			//signed this seed inactive....
			//printf("[%d] :: rx seed err %x\n",i, p_seed[i].rx_cmd_seed);
			p_seed[i].flag = INACTIVE_SEED;
		}
	}
	if(old_best_winlen == 0){
		printf("got no best tx cmd......all rx seed failed...maybe next time will got..\n");
		goto AUTO_SCAN_ERR_PROCESS;
	}
	emmc_pinfo->hs400_rx_data = best_rx_data_dl;
	printf("best hs400 rx data is [%x]\n",emmc_pinfo->hs400_rx_data);
	return 0;

AUTO_SCAN_ERR_PROCESS:
	return -1;
}


static void dump_result(void){
	int i;
	printf("dump result..\n");
	for(i = 0; i < MAX_AUTO_TEST_TIMES; i++){
		printf("[%02x] :: [%02x] :: [%02x] :: [%02x]\n",
		auto_dll_result_array[i].flag,
		auto_dll_result_array[i].tx_data,
		auto_dll_result_array[i].rx_cmd,
		auto_dll_result_array[i].rx_data
		);
	}
}


int cal_count(struct auto_dll_result *p, u32 array_size, u32 in_cal){
	int i;
	int ret_cnt = 0;
	for(i = 0; i < array_size; i++){
		if(p[i].flag == ACTIVE_RESULT){
			if(p[i].tx_data == in_cal){
				ret_cnt++;
			}
		}
	}
	return ret_cnt;
}

int process_result_array(struct auto_dll_result *p, u32 array_size, u32 *result_index){
	int best_tx_data = 0;
	int new_no_cnt;
	int old_no_cnt;
	int i;
	new_no_cnt = 0;
	old_no_cnt = 0;
	dump_result();

	//first check if all result err...
	for(i = 0; i < array_size; i++){
		if(p[i].flag == ACTIVE_RESULT){
			break;
		}
	}
	if(i == array_size){
		printf("result all err...\n");
		return -1;
	}

	for(i = 0; i < MAX_TX_DELAY_LINE_SIZE; i++){
		new_no_cnt = cal_count(p, array_size, i);
		//printf("val : cnt = %x : %d\n",i,new_no_cnt);
		if(old_no_cnt < new_no_cnt){
			//update
			old_no_cnt = new_no_cnt;
			best_tx_data = i;
		}
	}

	for(i = 0; i < array_size; i++){
		if(p[i].flag == ACTIVE_RESULT){
			if(p[i].tx_data == best_tx_data){
				*result_index = i;
				break;
			}
		}
	}

	return 0;
}

int bind_emmc_card_info(struct sdhci_host *host, struct mshc_lite_plat_info *plat_info){
	u8 emmc_name[16] = {0};
	u32 ret_cid[4] = {0};
	struct fh_mshc_lite *p_mshc;
	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;
	p_mshc = host->dri_pri;
	mshc_auto_get_mmc_cid(host, ret_cid, emmc_name);
	emmc_pinfo = emmc_name_to_plat_info(plat_info->emmc_info_list, (char *)emmc_name);
	if(!emmc_pinfo){
		printf("pls add board emmc[%s] info..\n",emmc_name);
		FH_MSHC_LITE_ASSERT(0);
		//goto ERR_PROCESS;
	}
	//here memcpy name to mmc name buf.
	strcpy(emmc_pinfo->mmc_name_buf, emmc_pinfo->mmc_name);
	printf("emmc_pinfo->mmc_name_buf is %s...\n",emmc_pinfo->mmc_name_buf);
	p_mshc->bind_active_tuning_emmc = emmc_pinfo;
	return 0;
}




int fh_mshc_phy_io_dll_auto_scan_hs(struct sdhci_host *host, struct mshc_lite_plat_info *plat_info){

	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;
	struct fh_mshc_lite *p_mshc;
	u32 tuning_mode;
	int ret;
	int i;
	u32 best_result_index;

	p_mshc = host->dri_pri;
	emmc_pinfo = p_mshc->bind_active_tuning_emmc;

	for(i = 0; i < sizeof(dll_seed_array) / sizeof(struct auto_dll_seed); i++){
		dll_seed_array[i].flag = ACTIVE_SEED;
		dll_seed_array[i].rx_cmd_seed = i * DLL_SCAN_TX_STEP;
		dll_seed_array[i].tx_cmd_ret = 0;
		//printf("[%d] :: seed = %x\n",i, dll_seed_array[i].rx_cmd_seed);
	}
	memset(auto_dll_result_array, 0 , sizeof(auto_dll_result_array));
	tuning_mode = AUTO_SCAN_HS;
	i = 0;
	while(i < MAX_AUTO_TEST_TIMES){
		printf("emmc auto tuning try[%d]\n",i);
		ret = __fh_mshc_phy_io_dll_auto_scan_hs_with_clk(host, plat_info, tuning_mode, dll_seed_array,
		sizeof(dll_seed_array) / sizeof(struct auto_dll_seed));
		if(ret == 0){
			//rec hs200 result..
			auto_dll_result_array[i].flag = ACTIVE_RESULT;
			auto_dll_result_array[i].tx_data = emmc_pinfo->hs_tx_data;
			auto_dll_result_array[i].rx_cmd = emmc_pinfo->hs_rx_cmd;
			auto_dll_result_array[i].rx_data = emmc_pinfo->hs_rx_data;
		}else{
			auto_dll_result_array[i].flag = INACTIVE_RESULT;
		}
		i++;
	}

	ret = process_result_array(auto_dll_result_array, MAX_AUTO_TEST_TIMES, &best_result_index);
	if(ret != 0){
		goto ERR_PROCESS;
	}
	//printf("best index is %x\n",best_result_index);
	emmc_pinfo->hs_tx_data = auto_dll_result_array[best_result_index].tx_data;
	emmc_pinfo->hs_rx_cmd = auto_dll_result_array[best_result_index].rx_cmd;
	emmc_pinfo->hs_rx_data = auto_dll_result_array[best_result_index].rx_data;

	printf("[%s]\n",emmc_pinfo->mmc_name);
	printf("hs '10 format' -->> tx data  : rx cmd : rx data =  [%04d] : [%04d] : [%04d]\n",
	emmc_pinfo->hs_tx_data, emmc_pinfo->hs_rx_cmd, emmc_pinfo->hs_rx_data);
	printf("hs '16 format' -->> tx data  : rx cmd : rx data =  [%04x] : [%04x] : [%04x]\n",
	emmc_pinfo->hs_tx_data, emmc_pinfo->hs_rx_cmd, emmc_pinfo->hs_rx_data);

	return 0;

ERR_PROCESS:
	return -1;
	//-1 : tell core not support hs200..
	//-2 : tell core not support hs400..
	//TBD just goto deadloop for now.
	//FH_MSHC_LITE_ASSERT(0);
}

int fh_mshc_phy_io_dll_auto_scan_hs200(struct sdhci_host *host, struct mshc_lite_plat_info *plat_info){
	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;
	struct fh_mshc_lite *p_mshc;
	u32 tuning_mode;
	int ret;
	int i;
	u32 best_result_index;

	p_mshc = host->dri_pri;
	emmc_pinfo = p_mshc->bind_active_tuning_emmc;

	for(i = 0; i < sizeof(dll_seed_array) / sizeof(struct auto_dll_seed); i++){
		dll_seed_array[i].flag = ACTIVE_SEED;
		dll_seed_array[i].rx_cmd_seed = i * DLL_SCAN_TX_STEP;
		dll_seed_array[i].tx_cmd_ret = 0;
		//printf("[%d] :: seed = %x\n",i, dll_seed_array[i].rx_cmd_seed);
	}
	memset(auto_dll_result_array, 0 , sizeof(auto_dll_result_array));
	tuning_mode = AUTO_SCAN_HS200;
	i = 0;
	while(i < MAX_AUTO_TEST_TIMES){
		printf("emmc auto tuning try[%d]\n",i);
		ret = __fh_mshc_phy_io_dll_auto_scan_hs200_with_clk(host, plat_info, tuning_mode, dll_seed_array,
		sizeof(dll_seed_array) / sizeof(struct auto_dll_seed));
		if(ret == 0){
			//rec hs200 result..
			auto_dll_result_array[i].flag = ACTIVE_RESULT;
			auto_dll_result_array[i].tx_data = emmc_pinfo->hs200_tx_data;
			auto_dll_result_array[i].rx_cmd = emmc_pinfo->hs200_rx_cmd;
			auto_dll_result_array[i].rx_data = emmc_pinfo->hs200_rx_data;
		}else{
			auto_dll_result_array[i].flag = INACTIVE_RESULT;
		}
		i++;
	}

	ret = process_result_array(auto_dll_result_array, MAX_AUTO_TEST_TIMES, &best_result_index);
	if(ret != 0){
		goto ERR_PROCESS;
	}
	//printf("best index is %x\n",best_result_index);
	emmc_pinfo->hs200_tx_data = auto_dll_result_array[best_result_index].tx_data;
	emmc_pinfo->hs200_rx_cmd = auto_dll_result_array[best_result_index].rx_cmd;
	emmc_pinfo->hs200_rx_data = auto_dll_result_array[best_result_index].rx_data;

	printf("[%s]\n",emmc_pinfo->mmc_name);
	printf("hs200 '10 format' -->> tx data  : rx cmd : rx data =  [%04d] : [%04d] : [%04d]\n",
	emmc_pinfo->hs200_tx_data, emmc_pinfo->hs200_rx_cmd, emmc_pinfo->hs200_rx_data);
	printf("hs200 '16 format' -->> tx data  : rx cmd : rx data =  [%04x] : [%04x] : [%04x]\n",
	emmc_pinfo->hs200_tx_data, emmc_pinfo->hs200_rx_cmd, emmc_pinfo->hs200_rx_data);

	return 0;

ERR_PROCESS:
	return -1;
	//-1 : tell core not support hs200..
	//-2 : tell core not support hs400..
	//TBD just goto deadloop for now.
	//FH_MSHC_LITE_ASSERT(0);
}





// int mshc_hs200_auto_classic_tuning(struct sdhci_host *host, struct rec_dl_info *p_info, int rec_dl_size,
// u32 init_rx_val, u32 step_size, u32 tuning_mode)


int fh_mshc_phy_io_dll_auto_scan_hs200_classic(struct sdhci_host *host, struct mshc_lite_plat_info *plat_info){

	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;
	struct fh_mshc_lite *p_mshc;
	u32 tuning_mode;
	int ret;
	int i;
	u32 best_result_index;

	p_mshc = host->dri_pri;
	emmc_pinfo = p_mshc->bind_active_tuning_emmc;

	for(i = 0; i < sizeof(dll_seed_array) / sizeof(struct auto_dll_seed); i++){
		dll_seed_array[i].flag = ACTIVE_SEED;
		dll_seed_array[i].rx_cmd_seed = i * DLL_SCAN_TX_STEP;
		dll_seed_array[i].tx_cmd_ret = 0;
		//printf("[%d] :: seed = %x\n",i, dll_seed_array[i].rx_cmd_seed);
	}
	memset(auto_dll_result_array, 0 , sizeof(auto_dll_result_array));
	tuning_mode = AUTO_SCAN_HS200;
	i = 0;
	while(i < MAX_AUTO_TEST_TIMES){
		printf("%s :: emmc auto tuning try[%d]\n",__func__,i);
		ret = __fh_mshc_phy_io_dll_auto_scan_hs200_classic(host, plat_info, tuning_mode, dll_seed_array,
		sizeof(dll_seed_array) / sizeof(struct auto_dll_seed));
		if(ret == 0){
			//rec hs200 result..
			auto_dll_result_array[i].flag = ACTIVE_RESULT;
			auto_dll_result_array[i].tx_data = emmc_pinfo->hs200_tx_data;
			auto_dll_result_array[i].rx_cmd = emmc_pinfo->hs200_rx_cmd;
			auto_dll_result_array[i].rx_data = emmc_pinfo->hs200_rx_data;
		}else{
			auto_dll_result_array[i].flag = INACTIVE_RESULT;
		}
		i++;
	}

	ret = process_result_array(auto_dll_result_array, MAX_AUTO_TEST_TIMES, &best_result_index);
	if(ret != 0){
		goto ERR_PROCESS;
	}
	//printf("best index is %x\n",best_result_index);
	emmc_pinfo->hs200_tx_data = auto_dll_result_array[best_result_index].tx_data;
	emmc_pinfo->hs200_rx_cmd = auto_dll_result_array[best_result_index].rx_cmd;
	emmc_pinfo->hs200_rx_data = auto_dll_result_array[best_result_index].rx_data;

	printf("[%s]\n",emmc_pinfo->mmc_name);
	printf("hs200 '10 format' -->> tx data  : rx cmd : rx data =  [%04d] : [%04d] : [%04d]\n",
	emmc_pinfo->hs200_tx_data, emmc_pinfo->hs200_rx_cmd, emmc_pinfo->hs200_rx_data);
	printf("hs200 '16 format' -->> tx data  : rx cmd : rx data =  [%04x] : [%04x] : [%04x]\n",
	emmc_pinfo->hs200_tx_data, emmc_pinfo->hs200_rx_cmd, emmc_pinfo->hs200_rx_data);

	return 0;

ERR_PROCESS:
	return -1;

}


int __fh_mshc_phy_io_dll_auto_scan_hs400_es(struct sdhci_host *host, struct mshc_lite_plat_info *plat_info){
	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;
	struct fh_mshc_lite *p_mshc;
	u32 best_rx_cmd_dl;
	u32 best_rx_data_dl;
	u32 best_tx_data_dl;
	u32 new_best_winlen;
	int ret;
	u32 step_size;

	p_mshc = host->dri_pri;
	emmc_pinfo = p_mshc->bind_active_tuning_emmc;

	step_size = 2;
	mshc_auto_hs400_es_rxcmd_dl_rec(host, dl_info, MAX_RX_DELAY_LINE_SIZE,
	emmc_pinfo->hs200_tx_data, step_size, 0);
	ret = emmc_parse_dl_rec(dl_info, MAX_RX_DELAY_LINE_SIZE, &best_rx_cmd_dl, PARSE_RX_DL_REC, &new_best_winlen);
	if(ret == 0){
		printf("got best hs400 es rx cmd dll is [0x%x]\n", best_rx_cmd_dl);
	}
	else{
		printf("failed got rx cmd dll...\n");
		goto ERR_PROCESS;
	}

	//best_rx_cmd_dl = 0x68;
	mshc_auto_hs400_es_rxdata_dl_rec(host, dl_info, MAX_RX_DELAY_LINE_SIZE,
	best_rx_cmd_dl, emmc_pinfo->hs200_tx_data, step_size);
	ret = emmc_parse_dl_rec(dl_info, MAX_RX_DELAY_LINE_SIZE, &best_rx_data_dl, PARSE_RX_DL_REC, &new_best_winlen);
	if(ret == 0){
		printf("got best hs400 es rx data dll is [0x%x]\n", best_rx_data_dl);
	}
	else{
		printf("failed got rx data dll...\n");
		goto ERR_PROCESS;
	}

	mshc_auto_hs400_es_txdata_dl_rec(host, dl_info, MAX_TX_DELAY_LINE_SIZE,
	best_rx_cmd_dl, best_rx_data_dl, step_size);
	ret = emmc_parse_dl_rec(dl_info, MAX_TX_DELAY_LINE_SIZE, &best_tx_data_dl, PARSE_TX_DL_REC, &new_best_winlen);
	if(ret == 0){
		printf("got best hs400 es tx data dll is [0x%x]\n", best_tx_data_dl);
	}
	else{
		printf("failed got tx data dll...\n");
		goto ERR_PROCESS;
	}
	//need auto fix test
	printf("go to hs400 es fix test...\n");
	ret = mshc_auto_hs400_es_fix_test(host, best_tx_data_dl, best_rx_cmd_dl,
	best_rx_data_dl, AUTO_FIX_TEST_PASS_TIME);
	if(ret != 0){
		printf("failed fix test...\n");
		goto ERR_PROCESS;
	}
	//emmc_pinfo->es_sup = 1;
	emmc_pinfo->hs400_tx_data = best_tx_data_dl;
	emmc_pinfo->hs400_rx_cmd = best_rx_cmd_dl;
	emmc_pinfo->hs400_rx_data = best_rx_data_dl;
	printf("[%s]\n",emmc_pinfo->mmc_name);
	printf("hs400 '16 format' -->> tx data  : rx cmd : rx data =  [%04x] : [%04x] : [%04x]\n",
	emmc_pinfo->hs400_tx_data, emmc_pinfo->hs400_rx_cmd, emmc_pinfo->hs400_rx_data);


	return 0;
ERR_PROCESS:
	//-1 : tell core not support hs200..
	//-2 : tell core not support hs400..
	//TBD just goto deadloop for now.
	//FH_MSHC_LITE_ASSERT(0);
	return -2;
}

int __fh_mshc_phy_io_dll_auto_scan_hs400_classics(struct sdhci_host *host, struct mshc_lite_plat_info *plat_info){

	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;
	struct fh_mshc_lite *p_mshc;
	int ret;
	int i;
	u32 best_result_index;


	p_mshc = host->dri_pri;
	emmc_pinfo = p_mshc->bind_active_tuning_emmc;

	for(i = 0; i < sizeof(hs400_dll_seed_array) / sizeof(struct auto_dll_seed); i++){
		hs400_dll_seed_array[i].flag = ACTIVE_SEED;
		hs400_dll_seed_array[i].rx_cmd_seed = i * DLL_SCAN_HS400_TX_STEP;
		hs400_dll_seed_array[i].tx_cmd_ret = 0;
		//printf("[%d] :: seed = %x\n",i, dll_seed_array[i].rx_cmd_seed);
	}
	memset(auto_dll_result_array, 0 , sizeof(auto_dll_result_array));
	//test all rx cmd seed ,and mark inactive seed..
	ret = __fh_mshc_auto_get_hs400_classic_rx_data(host, plat_info,  hs400_dll_seed_array,
		sizeof(hs400_dll_seed_array) / sizeof(struct auto_dll_seed));
	if(ret){
		printf("ERR : hs400 classic got no rx data......\n");
		goto ERR_PROCESS;
	}

	i = 0;
	while(i < MAX_AUTO_TEST_TIMES){
		printf("emmc hs400 classic auto tuning try[%d]\n",i);
		ret = __fh_mshc_auto_get_hs400_classic_tx_rxcmd(host, plat_info, hs400_dll_seed_array,
		sizeof(hs400_dll_seed_array) / sizeof(struct auto_dll_seed));
		if(ret == 0){
			//rec hs200 result..
			auto_dll_result_array[i].flag = ACTIVE_RESULT;
			auto_dll_result_array[i].tx_data = emmc_pinfo->hs400_tx_data;
			auto_dll_result_array[i].rx_cmd = emmc_pinfo->hs400_rx_cmd;
			auto_dll_result_array[i].rx_data = emmc_pinfo->hs400_rx_data;
		}else{
			auto_dll_result_array[i].flag = INACTIVE_RESULT;
		}
		i++;
	}

	ret = process_result_array(auto_dll_result_array, MAX_AUTO_TEST_TIMES, &best_result_index);
	if(ret != 0){
		goto ERR_PROCESS;
	}

	//printf("best index is %x\n",best_result_index);
	emmc_pinfo->hs400_tx_data = auto_dll_result_array[best_result_index].tx_data;
	emmc_pinfo->hs400_rx_cmd = auto_dll_result_array[best_result_index].rx_cmd;
	emmc_pinfo->hs400_rx_data = auto_dll_result_array[best_result_index].rx_data;

	printf("[%s]\n",emmc_pinfo->mmc_name);
	printf("hs400 '16 format' -->> tx data  : rx cmd : rx data =  [%04x] : [%04x] : [%04x]\n",
	emmc_pinfo->hs400_tx_data, emmc_pinfo->hs400_rx_cmd, emmc_pinfo->hs400_rx_data);

	#if(0)
	step_size = 2;
	mshc_auto_hs400_classic_rxdata_dl_rec(host, dl_info, MAX_RX_DELAY_LINE_SIZE,
	emmc_pinfo->hs200_rx_cmd, emmc_pinfo->hs200_tx_data, step_size);
	ret = emmc_parse_dl_rec(dl_info, MAX_RX_DELAY_LINE_SIZE, &best_rx_data_dl, PARSE_RX_DL_REC, &new_best_winlen);
	if(ret == 0){
		printf("got best hs400 classic rx data dll is [0x%x]\n", best_rx_data_dl);
	}
	else{
		printf("failed got rx data dll...\n");
		goto ERR_PROCESS;
	}
	#endif
	return 0;
ERR_PROCESS:
	return -2;
	//-1 : tell core not support hs200..
	//-2 : tell core not support hs400..
	//TBD just goto deadloop for now.
	//FH_MSHC_LITE_ASSERT(0);
	return 0;
}

void dump_emmc_cardinfo(struct mshc_lite_emmc_card_info *emmc_pinfo){
	printf("-------------------------------\n");
	printf("mmc_name      : %s\n",emmc_pinfo->mmc_name);
	printf("mmc_name_buf  : %s\n",emmc_pinfo->mmc_name_buf);
	printf("flag          : %08x\n",emmc_pinfo->flag);
	printf("magic         : %08x\n",emmc_pinfo->magic);
	printf("sup_mode      : %08x\n",emmc_pinfo->sup_mode);
	if(emmc_pinfo->sup_mode & MMC_MODE_HS400ES)
		printf("sup_max       : MMC_MODE_HS400ES\n");
	else if(emmc_pinfo->sup_mode & MMC_MODE_HS400_1_8V)
		printf("sup_max       : MMC_MODE_HS400_1_8V\n");
	else if(emmc_pinfo->sup_mode & MMC_MODE_HS200_1_8V)
		printf("sup_max       : MMC_MODE_HS200_1_8V\n");
	else
		printf("sup_max       : MMC_MODE_HS\n");
	printf("es_sup        : %08x\n",emmc_pinfo->es_sup);
	printf("hs_tx_data    : %08x\n",emmc_pinfo->hs_tx_data);
	printf("hs_rx_cmd     : %08x\n",emmc_pinfo->hs_rx_cmd);
	printf("hs_rx_data    : %08x\n",emmc_pinfo->hs_rx_data);
	printf("hs200_tx_data : %08x\n",emmc_pinfo->hs200_tx_data);
	printf("hs200_rx_cmd  : %08x\n",emmc_pinfo->hs200_rx_cmd);
	printf("hs200_rx_data : %08x\n",emmc_pinfo->hs200_rx_data);	
	printf("hs400_tx_data : %08x\n",emmc_pinfo->hs400_tx_data);
	printf("hs400_rx_cmd  : %08x\n",emmc_pinfo->hs400_rx_cmd);
	printf("hs400_rx_data : %08x\n",emmc_pinfo->hs400_rx_data);	
	printf("-------------------------------\n");
}

int fh_mshc_auto_check_ext_reg(struct sdhci_host *host, u32 *hs400_sup, u32 *es_sup, u8 *ext_data){
	int ret;
	u8 tmp_u8 = 0;

	
	memset(ext_data, 0, 512);
	ret = mshc_auto_get_mmc_ext(host, ext_data);
	if(ret){
		printf("%s :: err :%x\n", __func__, ret);
		return ret;
	}
	//process ext data...
	*hs400_sup = 1;
	if((ext_data[EXT_CSD_CARD_TYPE] & (1 << 6)) == 0){
		printf("not support hs400 1.8v mode\n");
		//return -1;
		*hs400_sup = 0;
	}

	tmp_u8 = ext_data[EXT_CSD_STROBE_SUPPORT];
	printf("support %s hs400 mode\n", 
	tmp_u8 == 1 ? "es" : "classics");
	*es_sup = 0;
	if(tmp_u8 == 1)
		*es_sup = 1;

	//debug
	//*es_sup = 0;
	printf("TYPE_B life :: %s\n",mmc_life_str[ext_data[269]]);
	printf("TYPE_A life :: %s\n",mmc_life_str[ext_data[268]]);
	//dump_est_csd_reg(ext_data);

	return 0;
}

int fh_mshc_phy_io_dll_auto_scan_hs400(struct sdhci_host *host, struct mshc_lite_plat_info *plat_info, u32 es_sup){
	int ret;
	//debug
	//es_sup = 0;
	if(es_sup){
		ret = __fh_mshc_phy_io_dll_auto_scan_hs400_es(host, plat_info);
	}
	else{
		ret = __fh_mshc_phy_io_dll_auto_scan_hs400_classics(host, plat_info);
	}
	return ret;
}

static int mmc_auto_tuning_hs200(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]){
	int times = 0;
	int i;
	times = simple_strtoul(argv[1], NULL, 16);
	printf("auto times [%x] go ~~~~~~~\n",times);	
	for(i = 0; i < times; i++){

		fh_mshc_phy_io_dll_auto_scan_hs200(g_fh_mshc_obj->bind_host, g_fh_mshc_obj->plat);
	}
	return 0;
}


U_BOOT_CMD(
 mmc_auto_tuning_hs200, 4, 0, mmc_auto_tuning_hs200,
 "just auto tuning~~~~~",
 ""
);



static int mmc_auto_tuning_hs(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]){
	int times = 0;
	int i;
	times = simple_strtoul(argv[1], NULL, 16);
	printf("auto times [%x] go ~~~~~~~\n",times);	
	for(i = 0; i < times; i++){
		fh_mshc_phy_io_dll_auto_scan_hs(g_fh_mshc_obj->bind_host, g_fh_mshc_obj->plat);
	}
	return 0;
}


U_BOOT_CMD(
 mmc_auto_tuning_hs, 4, 0, mmc_auto_tuning_hs,
 "just auto tuning~~~~~",
 ""
);


static int mmc_hs400_classic_auto_tuning(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]){
	int times = 0;
	int i;
	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;
	struct fh_mshc_lite *p_mshc;


	p_mshc = g_fh_mshc_obj->bind_host->dri_pri;
	emmc_pinfo = p_mshc->bind_active_tuning_emmc;

	if(argc != 3)
	{
		printf("times : tx_data \n");
		return -1;
	}

	times = simple_strtoul(argv[1], NULL, 16);
	emmc_pinfo->hs200_tx_data = simple_strtoul(argv[2], NULL, 16);

	printf("auto times [%x] go ~~~~~~~\n",times);
	for(i = 0; i < times; i++){
		__fh_mshc_phy_io_dll_auto_scan_hs400_classics(g_fh_mshc_obj->bind_host, g_fh_mshc_obj->plat);
	}
	return 0;
}


U_BOOT_CMD(
 mmc_hs400_classic_auto_tuning, 4, 0, mmc_hs400_classic_auto_tuning,
 "just auto tuning~~~~~",
 ""
);

static int mmc_hs400_es_auto_tuning(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]){
	int times = 0;
	int i;
	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;
	struct fh_mshc_lite *p_mshc;

	p_mshc = g_fh_mshc_obj->bind_host->dri_pri;
	emmc_pinfo = p_mshc->bind_active_tuning_emmc;
	if(argc != 3)
	{
		printf("times : tx_data \n");
		return -1;
	}
	times = simple_strtoul(argv[1], NULL, 16);
	emmc_pinfo->hs200_tx_data = simple_strtoul(argv[2], NULL, 16);
	printf("auto times [%x] go ~~~~~~~\n",times);
	for(i = 0; i < times; i++){
		__fh_mshc_phy_io_dll_auto_scan_hs400_es(g_fh_mshc_obj->bind_host, g_fh_mshc_obj->plat);
	}
	return 0;
}

U_BOOT_CMD(
 mmc_hs400_es_auto_tuning, 4, 0, mmc_hs400_es_auto_tuning,
 "just auto tuning~~~~~",
 ""
);

void fh_mshc_lite_hw_reset(struct fh_mshc_lite *fh_mshc_obj){
	//ref 6.15.10 H/W Reset Operation
	//__1us__::--200us--::emmc card ok
	//here keep rst_n 10us lowlev, then wait emmc for 500us
	reset_card_setting(fh_mshc_obj , 1);
	udelay(10);
	reset_card_setting(fh_mshc_obj , 0);
	udelay(500);
}


int fh_mshc_auto_tuning_pata_get(struct sdhci_host *host){
	int ret;
	struct fh_mshc_lite *fh_mshc_obj;
	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;
	struct mshc_lite_emmc_card_info *temp_pinfo = 0;

	fh_mshc_obj = (struct fh_mshc_lite *)host->dri_pri;
	emmc_pinfo = fh_mshc_obj->bind_active_tuning_emmc;
	//debug
	//return AUTO_TUNING_PARA_NULL;
	ret = mshc_auto_get_emmc_tuning_data(host, g_mshc_buf);
	if(ret){
		printf("auto get emmc tuning para err...try auto tuning...\n");
		return AUTO_TUNING_PARA_NULL;
	}
	//here just force to card info..
	temp_pinfo = (struct mshc_lite_emmc_card_info *)g_mshc_buf;

	if(temp_pinfo->magic == AUTO_TUNING_DONE){
		printf("got tuning done para.....\n");
		memcpy(emmc_pinfo, temp_pinfo, sizeof(struct mshc_lite_emmc_card_info));
		//bind name to self..
		emmc_pinfo->mmc_name = emmc_pinfo->mmc_name_buf;
		host->host_caps = emmc_pinfo->sup_mode;
		//dump got info...
		dump_emmc_cardinfo(emmc_pinfo);
		return AUTO_TUNING_PARA_ALREADY_GOT;
	}

	return AUTO_TUNING_PARA_NULL;
}

void fh_mshc_auto_tuning_pata_modify(struct sdhci_host *host){
	int ret;
	struct fh_mshc_lite *fh_mshc_obj;
	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;

	fh_mshc_obj = (struct fh_mshc_lite *)host->dri_pri;
	emmc_pinfo = fh_mshc_obj->bind_active_tuning_emmc;
	emmc_pinfo->magic = AUTO_TUNING_DONE;
	memcpy(g_mshc_buf, emmc_pinfo, sizeof(struct mshc_lite_emmc_card_info));
	printf("go to write modify data......\n");
	dump_emmc_cardinfo(emmc_pinfo);
	ret = mshc_auto_write_emmc_tuning_data(host, g_mshc_buf);
	if(ret){
		printf("modify emmc tuning para err....maybe next time goto auto tuning again..\n");
	}
}


int fh_mshc_auto_tuning_process(struct sdhci_host *host, struct mshc_lite_plat_info *plat_info, u32 hs400_sup, u32 es_sup){
	int ret;
	struct fh_mshc_lite *fh_mshc_obj;
	struct mshc_lite_emmc_card_info *emmc_pinfo = 0;

	fh_mshc_obj = (struct fh_mshc_lite *)host->dri_pri;
	emmc_pinfo = fh_mshc_obj->bind_active_tuning_emmc;


	ret = fh_mshc_phy_io_dll_auto_scan_hs(host, plat_info);
	if(ret == 0){
		host->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT | MMC_MODE_8BIT;
	}
	else{
		printf("hs tuning failed...pls check emmc first.\n");
		return -1;
	}

	ret = fh_mshc_phy_io_dll_auto_scan_hs200(host, plat_info);
	if(ret == 0){
		host->host_caps = MMC_MODE_HS200_1_8V | MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT | MMC_MODE_8BIT;
	}
	else{
		printf("try classic hs200 tuning test....\n");
		ret = fh_mshc_phy_io_dll_auto_scan_hs200_classic(host, plat_info);
		if(ret == 0){
			host->host_caps = MMC_MODE_HS200_1_8V | MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT | MMC_MODE_8BIT;
		}
		else{
			host->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT | MMC_MODE_8BIT;
		}
		goto FUNC_OUT;
	}
	//debug
	//hs400_sup = 0;
	if(!hs400_sup)
		goto FUNC_OUT;
	//only hs200 test pass,then check hs400..
	if(ret == 0){
		ret = fh_mshc_phy_io_dll_auto_scan_hs400(host, plat_info, es_sup);
		if(ret == 0){
			if(es_sup){
				host->host_caps = MMC_MODE_HS400ES | MMC_MODE_HS400_1_8V | MMC_MODE_HS200_1_8V |
				MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT | MMC_MODE_8BIT;
			}
			else{
				host->host_caps = MMC_MODE_HS400_1_8V | MMC_MODE_HS200_1_8V |
				MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT | MMC_MODE_8BIT;
			}
			emmc_pinfo->es_sup = es_sup;
		}
	}

FUNC_OUT:
	emmc_pinfo->sup_mode = host->host_caps;
	return 0;
}

void refix_force_setting(struct sdhci_host *host, struct mshc_lite_plat_info *plat_info){
	struct mshc_lite_emmc_card_info *emmc_pinfo;
	struct fh_mshc_lite *fh_mshc_obj;

	fh_mshc_obj = (struct fh_mshc_lite *)host->dri_pri;
	emmc_pinfo = fh_mshc_obj->bind_active_tuning_emmc;

	if(emmc_pinfo->sup_mode & MMC_MODE_HS400ES){
		host->host_caps = MMC_MODE_HS400ES | MMC_MODE_HS400_1_8V | MMC_MODE_HS200_1_8V |
		MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT | MMC_MODE_8BIT;
		emmc_pinfo->es_sup = 1;
	}
	else if(emmc_pinfo->sup_mode & MMC_MODE_HS400_1_8V){
		host->host_caps = MMC_MODE_HS400_1_8V | MMC_MODE_HS200_1_8V |
		MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT | MMC_MODE_8BIT;
		emmc_pinfo->es_sup = 0;
	}else if(emmc_pinfo->sup_mode & MMC_MODE_HS200_1_8V){
		host->host_caps = MMC_MODE_HS200_1_8V | MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT | MMC_MODE_8BIT;
	}
	else{
		host->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT | MMC_MODE_8BIT;
	}

	emmc_pinfo->sup_mode = host->host_caps;
}


int fh_mshc_lite_int(struct mshc_lite_plat_info *plat_info){

	int ret;
	struct sdhci_host *host = NULL;
	struct mmc *mmc = NULL;
	int dev_num;
	u32 es_sup;
	u32 hs400_sup;

	struct fh_mshc_lite *fh_mshc_obj = 0;
	fh_mshc_obj = malloc(sizeof(struct fh_mshc_lite));
	if(!fh_mshc_obj){
		printf("no mem alloc mshc ctrl...\n");
		return -ENOMEM;
	}

	host = calloc(1, sizeof(struct sdhci_host));
	if (!host) {
		printf("sdhci_host malloc fail!\n");
		free(fh_mshc_obj);
		return -ENOMEM;
	}

	host->name = plat_info->name;
	host->ioaddr = (void *)plat_info->ctrl_regs;
	host->quirks = SDHCI_QUIRK_32BIT_DMA_ADDR;
	host->ops = &dri_ops;
	host->dri_pri = fh_mshc_obj;
	fh_mshc_obj->ctrl_regs = (void*)plat_info->ctrl_regs;
	fh_mshc_obj->phy_regs = (void*)plat_info->phy_regs;
	fh_mshc_obj->plat = plat_info;
	fh_mshc_obj->bind_host = host;
	g_fh_mshc_obj = fh_mshc_obj;
	printf("-----------------------------------\n");
	fh_mshc_lite_hw_reset(fh_mshc_obj);
	bind_emmc_card_info(host, plat_info);
	ret = fh_mshc_auto_check_ext_reg(host, &hs400_sup ,&es_sup, g_mshc_ext_data);
	if(ret){
		printf("here can't be err.....check hw phase....\n");
		FH_MSHC_LITE_ASSERT(0);
	}

	if(fh_mshc_obj->bind_active_tuning_emmc->flag != FORCE_USR_SETTING){
		if(fh_mshc_auto_tuning_pata_get(host) == AUTO_TUNING_PARA_NULL){
			printf("using auto tuning\n");
			ret = fh_mshc_auto_tuning_process(host, plat_info, hs400_sup, es_sup);
			if(ret){
				printf("fh_mshc_auto_tuning_process failed .....\n");
				goto init_err_pro;
			}
			fh_mshc_auto_tuning_pata_modify(host);
		}
	}
	else{
		printf("using usr force setting...\n");
		refix_force_setting(host, plat_info);
	}

	fh_mshc_lite_hw_reset(fh_mshc_obj);
	//add use adma and auto stop func.
	host->flags = USE_ADMA | SDHCI_AUTO_CMD12;
	if(plat_info->soc_clk_init)
		plat_info->soc_clk_init(host);
	if(plat_info->soc_ctrl_reset)
		plat_info->soc_ctrl_reset(host);

	mshc_phy_init(fh_mshc_obj, INIT_INV_DISABLE);
	dev_num = get_mmc_num();
	//printf("%s :: dev num is %x\n",__func__,dev_num);
	//add sdhci will ++mmc dev num...
	add_sdhci(host, FH_SDHCI_MAX_FREQ, FH_SDHC_MIN_FREQ);


	mmc = find_mmc_device(dev_num);
	if (!mmc) {
		printf("emmc device not found!!\n");
		return -EINVAL;
	}

	ret = mmc_init(mmc);
	if (ret)
		return ret;
	print_mmcinfo(mmc);
	//printf("at last dev no is %x\n",get_mmc_num());
	return 0;

init_err_pro:
	free(host);
	free(fh_mshc_obj);
}

